//
// Copyright (C) OpenSim Ltd.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//

#include <vector>
#include "Router.h"
#include "cluster/basic/ai_sim.h"
#include "cluster/roce/routing_adapter.h"

using namespace ai_sim;
class RoutingAdapter;

namespace inet {

Define_Module(Router);

void Router::initialize() {
    enablePFC = par("enablePFC");
    enableECN = par("enableECN");
    cModule * routing_adapter_mod = getModuleByPath("^.adapter");
    assert(routing_adapter_mod != nullptr);
    if (routing_adapter_mod == nullptr) error("stop");
    routing_adapter =
        omnetpp::check_and_cast<ai_sim::RoutingAdapter *>(routing_adapter_mod);
}

// 添加ACL表路由项
void Router::addACLEntry(ACLEntry ae) {
    ACLTable.push_back(ae);
}

// Need to add switch ack and find route in adapter
void Router::handleMessage(omnetpp::cMessage *msg) {
    // EV << "here" << endl;
    if (msg->arrivedOn("controlPort$i")) {
        // from configuration_port
        // EV << "Start change routes" << endl;
        ai_sim::RouteVecMsg *ra_msg = dynamic_cast<ai_sim::RouteVecMsg *> (msg);
        std::vector<ai_sim::ACLSingleEntry> ase_vec;
        if (ra_msg->getTargetId() != getId())
            error("Error route alloc msg on router");
        if (ra_msg->getNew_route_table().empty())
            error("empty route info to router");
        routing_adapter->getACL(
            ase_vec, ra_msg->getNew_route_table(), getId());
        // std::cout << "Get ACL on " << getFullPath() << endl;
        if (ase_vec.empty()) error("empty acl info to router");
        for (auto ase : ase_vec) {
            ACLEntry ae;
            ae.SourceAddress = inet::Ipv4Address(192,168,(ase.SourceAddressInt +1) / 256,(ase.SourceAddressInt +1) % 256);
            ae.DestAddress = inet::Ipv4Address(192,168, (ase.DestAddress +1) / 256, (ase.DestAddress +1) % 256);
            ae.NextHopAddress = inet::Ipv4Address(ase.NextHopAddress);
            ae.SourcePort = ase.SourcePort;
            ae.interfaceID = ase.interfaceID;
            ae.priority = ase.priority;
            ae.protocolID = ase.protocolID;
            entryToForward = ae;
            addACLEntry(ae);
            needReadACLTable = true;
            if (ae.interfaceID == 0) {
                std::cout << "*******************************" << endl;
                std::cout << "I am " << getFullPath() << endl;
                std::cout << "SourceAddress in ACL" << ae.SourceAddress << endl;
                std::cout  << "DestAddress in ACL" <<  ae.DestAddress << endl;
                std::cout  << "NextHopAddress in ACL" <<
                    inet::Ipv4Address(ase.NextHopAddress) << endl;
                std::cout  << "SourcePort in ACL" <<  ase.SourcePort << endl;
                std::cout  << "interfaceID in ACL" <<  ase.interfaceID << endl;
                error("stop");
            }
        }
        // SwitchAckMsg* sa_msg = new SwitchAckMsg;
        // sa_msg->setThe_flow(ra_msg->getThe_flow());
        // sa_msg->setKind(SWITCH_ACK_MSG);
        // send(sa_msg, "controlPort$o");
        delete ra_msg;
    }
    //TODO handle message from controller needReadACLTable
    else
        throw omnetpp::cRuntimeError("Unknown gate");
    }

}   // namespace inet

